import { useEffect } from 'react'
import { useRequest, useMemoizedFn, useUpdateEffect } from 'ahooks'
import { handleData, getUGM } from '@/utils/handlePrimitive'
import store from '@/store'
import { TextureLoader } from 'three'
import { getTypeOfVariable } from '@/utils/tools'
import { v4 as uuidv4 } from 'uuid'
import { cloneDeep } from 'lodash-es'

const useRxdb = () => {
  const [graphState, graphDispatchers] = store.useModel('graph')
  const { ugmId, data, bim, projectData } = graphState

  useUpdateEffect(() => {
    init()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [projectData])

  useEffect(() => {
    const ugm = getUGM({ data: data, bim: bim })
    if (ugm && data && data.cols && data.cols.point.length > 0) {
      graphDispatchers.saveData({ data: ugm, type: 'ugm' })
    }
  }, [data, bim, graphDispatchers])

  const { run, cancel } = useRequest(graphDispatchers.getData, {
    manual: true,
    onSuccess: res => {
      if (res) {
        const data = handleData(res)
        if (data) addData(data)
      } else {
        graphDispatchers.update({ loadingVisible: false })
      }
    },
    onError: error => {
      cancel()
      graphDispatchers.update({ loadingVisible: false })
      console.log(error)
    }
  })

  const { run: getDrawingLayoutPoint, cancel: cancelDrawingLayoutPoint } = useRequest(
    graphDispatchers.getDrawingLayoutPoint,
    {
      manual: true,
      onSuccess: res => {
        if (res && res.data && getTypeOfVariable(res.data) === 'Object') {
          const data = res.data
          const ricentDrawing = {
            ugmId: ugmId,
            concrete: 'data:image/png;base64,' + data.drawing_concrete_base64,
            layout: data.drawing_layout,
            masonry: 'data:image/png;base64,' + data.drawing_masonry_plaster_base64,
            point: data.drawing_point
          }
          Promise.all([
            new TextureLoader().load(ricentDrawing.masonry),
            new TextureLoader().load(ricentDrawing.concrete)
          ])
            .then(arr => {
              const [masonry, concrete] = arr
              ricentDrawing.masonry = masonry
              ricentDrawing.concrete = concrete
              graphDispatchers.update({ ricentDrawing })
            })
            .catch(e => {
              console.log(e)
            })
        }
      },
      onError: error => {
        cancelDrawingLayoutPoint()
        console.log(error)
      }
    }
  )

  const { run: getDrawingLayoutPointFromBackup, cancel: cancelDrawingLayoutPointFromBackup } = useRequest(
    graphDispatchers.getDrawingLayoutPointFromBackup,
    {
      manual: true,
      onSuccess: res => {
        if (res && res.data && getTypeOfVariable(res.data) === 'Object') {
          const data = res.data
          const ricentDrawingBackup = {
            ugmId: ugmId,
            concrete: 'data:image/png;base64,' + data.drawing_concrete_base64,
            layout: data.drawing_layout,
            masonry: 'data:image/png;base64,' + data.drawing_masonry_plaster_base64,
            point: data.drawing_point
          }
          const img = new Image()
          img.src = ricentDrawingBackup.masonry
          img.onload = () => {
            Promise.all([
              new TextureLoader().load(ricentDrawingBackup.masonry),
              new TextureLoader().load(ricentDrawingBackup.concrete)
            ])
              .then(arr => {
                const [masonry, concrete] = arr
                ricentDrawingBackup.masonry = masonry
                ricentDrawingBackup.concrete = concrete
                ricentDrawingBackup.w = img.width
                ricentDrawingBackup.h = img.height
                graphDispatchers.update({ ricentDrawingBackup })
              })
              .catch(e => {
                console.log(e)
              })
          }
        }
      },
      onError: error => {
        cancelDrawingLayoutPointFromBackup()
        console.log(error)
      }
    }
  )

  const init = useMemoizedFn(() => {
    const data = addTitle('data')
    const bim = addTitle('bim')
    if (!data.cols.entity || data.cols.entity.length === 0) {
      data.cols.entity = [{ name: 'entity01', id: uuidv4(), definition: 'entity' }]
    }
    if (!bim.cols.bim || bim.cols.bim.length === 0) {
      bim.cols.bim = [{ name: 'bim01', id: uuidv4(), definition: 'bim' }]
    }
    graphDispatchers.update({ data, bim, loadingVisible: true })
    run()
    getDrawingLayoutPoint()
    getDrawingLayoutPointFromBackup()
  })

  const addData = useMemoizedFn(_data => {
    const data = addTitle('data')
    const bim = addTitle('bim')
    if (!_data.cols.entity || _data.cols.entity.length === 0) {
      _data.cols.entity = [{ name: 'entity01', id: uuidv4(), definition: 'entity' }]
    }
    if (!_data.cols.bim || _data.cols.bim.length === 0) {
      _data.cols.bim = [{ name: 'bim01', id: uuidv4(), definition: 'bim' }]
    }
    for (const key in _data.cols) {
      if (isBIM(key)) {
        bim.cols[key] = _data.cols[key]
      } else {
        data.cols[key] = _data.cols[key]
      }
    }
    for (const key in data.cols) {
      data.cols[key].forEach(doc => {
        data.docs[doc.id] = doc
      })
    }
    for (const key in bim.cols) {
      bim.cols[key].forEach(doc => {
        bim.docs[doc.id] = doc
      })
    }
    graphDispatchers.update({ data, bim, loadingVisible: false })
  })

  const addDocs = useMemoizedFn(docs => {
    const updatedAt = Date.now()
    const data = cloneDeep(graphState.data)
    const bim = cloneDeep(graphState.bim)

    docs.forEach(doc => {
      if (!isBIM(doc.definition)) {
        data.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      } else {
        bim.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      }
    })

    data.cols = addTitle('data').cols
    for (const key in data.docs) {
      const node = data.docs[key]
      const definition = node.definition
      if (definition) {
        if (!data.cols[definition]) data.cols[definition] = []
        data.cols[definition].push(node)
      }
    }

    bim.cols = addTitle('bim').cols
    for (const key in bim.docs) {
      const node = bim.docs[key]
      const definition = node.definition
      if (definition) {
        if (!bim.cols[definition]) bim.cols[definition] = []
        bim.cols[definition].push(node)
      }
    }

    graphDispatchers.update({ data, bim })
  })

  const deleteDocs = useMemoizedFn(ids => {
    if (!ids.length) return
    const data = cloneDeep(graphState.data)
    const bim = cloneDeep(graphState.bim)
    // 数组长度不变, 被删除的元素变为 undefined
    ids.forEach(id => {
      if (data.docs[id]) delete data.docs[id]
      if (bim.docs[id]) delete bim.docs[id]
    })

    data.cols = addTitle('data').cols
    for (const key in data.docs) {
      const node = data.docs[key]
      const definition = node.definition
      if (definition) {
        if (!data.cols[definition]) data.cols[definition] = []
        data.cols[definition].push(node)
      }
    }

    bim.cols = addTitle('bim').cols
    for (const key in bim.docs) {
      const node = bim.docs[key]
      const definition = node.definition
      if (definition) {
        if (!bim.cols[definition]) bim.cols[definition] = []
        bim.cols[definition].push(node)
      }
    }
    graphDispatchers.update({ data, bim })
  })

  const updateDocs = useMemoizedFn(docs => {
    const updatedAt = Date.now()
    const data = cloneDeep(graphState.data)
    const bim = cloneDeep(graphState.bim)

    docs.forEach(doc => {
      if (!isBIM(doc.definition)) {
        data.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      } else {
        bim.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      }
    })

    data.cols = addTitle('data').cols
    for (const key in data.docs) {
      const node = data.docs[key]
      const definition = node.definition
      if (definition) {
        if (!data.cols[definition]) data.cols[definition] = []
        data.cols[definition].push(node)
      }
    }

    bim.cols = addTitle('bim').cols
    for (const key in bim.docs) {
      const node = bim.docs[key]
      const definition = node.definition
      if (definition) {
        if (!bim.cols[definition]) bim.cols[definition] = []
        bim.cols[definition].push(node)
      }
    }

    graphDispatchers.update({ data, bim })
  })

  const changeDocs = useMemoizedFn(docs => {
    const updatedAt = Date.now()
    const data = cloneDeep(graphState.data)
    const bim = cloneDeep(graphState.bim)

    docs.deleteData.forEach(id => {
      if (data.docs[id]) delete data.docs[id]
      if (bim.docs[id]) delete bim.docs[id]
    })

    docs.addData.forEach(doc => {
      if (!isBIM(doc.definition)) {
        data.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      } else {
        bim.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      }
    })

    docs.updateData.forEach(doc => {
      if (!isBIM(doc.definition)) {
        data.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      } else {
        bim.docs[doc.id] = { ...doc, _id: doc.id, updatedAt }
      }
    })

    data.cols = addTitle('data').cols
    for (const key in data.docs) {
      const node = data.docs[key]
      const definition = node.definition
      if (definition) {
        if (!data.cols[definition]) data.cols[definition] = []
        data.cols[definition].push(node)
      }
    }

    bim.cols = addTitle('bim').cols
    for (const key in bim.docs) {
      const node = bim.docs[key]
      const definition = node.definition
      if (definition) {
        if (!bim.cols[definition]) bim.cols[definition] = []
        bim.cols[definition].push(node)
      }
    }

    graphDispatchers.update({ data, bim })
  })

  const refresh = useMemoizedFn(() => init())

  const removeDb = useMemoizedFn(() => {
    const data = addTitle('data')
    const bim = addTitle('bim')
    // graphDispatchers.update({ data, bim, ugmId: uuidv4().substr(1, 6) })
    graphDispatchers.update({ data, bim })
  })

  const addTitle = useMemoizedFn(type => {
    const title = [
      'timestamp',
      'point',
      'line',
      'rendermaterial',
      'layer',
      'block',
      'polyline',
      'solid',
      'arc',
      'ellipse',
      'circle',
      'text',
      'opening',
      'window',
      'door',
      'room',
      'wall',
      'drywall',
      'mainwall',
      'beam',
      'column',
      'scanstation',
      'auto_plan_task',
      'apartment',
      'polylinechildren',
      'bim',
      'entity',
      'boundingbox',
      'change',
      'separator'
    ]
    if (type === 'data') {
      const data = { cols: {}, docs: {} }
      title.forEach(i => {
        if (!isBIM(i) && !data.cols[i]) data.cols[i] = []
      })
      return data
    } else {
      const bim = { cols: {}, docs: {} }
      title.forEach(i => {
        if (isBIM(i) && !bim.cols[i]) bim.cols[i] = []
      })
      return bim
    }
  })

  const isBIM = key => {
    if (
      key === 'apartment' ||
      key === 'room' ||
      key === 'door' ||
      key === 'window' ||
      key === 'opening' ||
      key === 'scanstation' ||
      key === 'auto_plan_task' ||
      key === 'bim' ||
      key === 'mainwall' ||
      key === 'drywall' ||
      key === 'beam' ||
      key === 'wall' ||
      key === 'column' ||
      key === 'separator'
    ) {
      return true
    } else {
      return false
    }
  }

  return {
    deleteDocs,
    addDocs,
    updateDocs,
    changeDocs,
    addData,
    refresh,
    removeDb
  }
}

export default useRxdb
